#include "csgnode.h"
#include "GL/glu.h"
#include "../gl2ps.h"
#include <string.h>
#include <QGLWidget>

const double MIN_SCALE = 0.01;
double boxColor[BOX_POS_END][4] = {
    {0.2, 0.2, 0.2, 0.5},
    {0.6, 0.6, 1.0, 0.5},
    {0.2, 0.2, 1.0, 1.0},
    {1.0, 0.0, 0.0, 1.0},
    {0.0, 0.0, 0.0, 0.5},
    {1.0, 1.0, 0.1, 0.5}
};

void drawBox(TreeNode<StarTestBox>* node)
{
    if (node->getNumChild() > 0)
    {
        return;
    }

    StarTestBox * box = node->getData();

    if (box->isOuter())
    {
        return;
    }

    glColor4dv(boxColor[box->pos]);
    glBegin(GL_POLYGON);
    glNormal3d(0, 0, -1);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y1, box->z0);
    glVertex3d(box->x0, box->y1, box->z0);
    glEnd();
    glBegin(GL_POLYGON);
    glNormal3d(0, 0, 1);
    glVertex3d(box->x0, box->y0, box->z1);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glEnd();
    glBegin(GL_POLYGON);
    glNormal3d(-1, 0, 0);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x0, box->y0, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z0);
    glEnd();
    glBegin(GL_POLYGON);
    glNormal3d(1, 0, 0);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x1, box->y1, box->z0);
    glEnd();
    glBegin(GL_POLYGON);
    glNormal3d(0, -1, 0);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x0, box->y0, box->z1);
    glEnd();
    glBegin(GL_POLYGON);
    glNormal3d(0, 1, 0);
    glVertex3d(box->x0, box->y1, box->z0);
    glVertex3d(box->x1, box->y1, box->z0);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glEnd();

    if (box->pos == BOX_STAR)
    {
        glColor3f(1.0, 0.0, 0.0);
        glPointSize(3.0);
        glBegin(GL_POINTS);
        glVertex2d((box->x0 + box->x1)/2, (box->y0 + box->y1)/2);
        glEnd();
        glPointSize(1.0);
    }
}

void drawBoxBound(TreeNode<StarTestBox> *node)
{
    if (node->getNumChild() > 0)
    {
        return;
    }

    StarTestBox * box = node->getData();

    glDisable(GL_LIGHTING);
    // glColor4dv(boxColor[box->pos]);
    glColor4d(0.2, 0.2, 0.2, 0.1);
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y1, box->z0);
    glVertex3d(box->x0, box->y1, box->z0);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x0, box->y0, box->z1);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x0, box->y0, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z0);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x1, box->y1, box->z0);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x0, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z0);
    glVertex3d(box->x1, box->y0, box->z1);
    glVertex3d(box->x0, box->y0, box->z1);
    glEnd();
    glBegin(GL_LINE_LOOP);
    glVertex3d(box->x0, box->y1, box->z0);
    glVertex3d(box->x1, box->y1, box->z0);
    glVertex3d(box->x1, box->y1, box->z1);
    glVertex3d(box->x0, box->y1, box->z1);
    glEnd();
}

CSGNode::CSGNode(int a, TreeNode<int> *parent)
    :TreeNode<int>(a, parent)
{
    emptyTestTree = NULL;
    viewBox = StarTestBox(0, 0, 0, 0);
}

CSGNode::~CSGNode()
{
    if (emptyTestTree)
    {
        delete emptyTestTree;
    }
}

void CSGNode::setRotation(double _rot)
{
    for (int i = 0; i < getNumChild(); i++)
    {
        ((CSGNode*)getChild(i))->setRotation(_rot);
    }
}

void CSGNode::setTranslation(double *tr)
{
    setTranslation(tr[0], tr[1]);
}

void CSGNode::setTranslation(double x, double y)
{
    for (int i = 0; i < getNumChild(); i++)
    {
        ((CSGNode*)getChild(i))->setTranslation(x, y);
    }
}

void CSGNode::setScale(double *s)
{
    setScale(s[0], s[1]);
}

void CSGNode::setScale(double x, double y)
{
    x = std::max(x, MIN_SCALE);
    y = std::max(y, MIN_SCALE);

    for (int i = 0; i < getNumChild(); i++)
    {
        ((CSGNode*)getChild(i))->setScale(x, y);
    }
}

void CSGNode::initial(StarTestBox &box, int dim)
{
    viewBox = box;

    if (emptyTestTree)
    {
        delete emptyTestTree;
    }

    TreeNode<StarTestBox> * root = new TreeNode<StarTestBox>(viewBox);
    emptyTestTree = new Tree<StarTestBox>(root);

    emptyTest(root);
}

void CSGNode::emptyTest(TreeNode<StarTestBox> *node)
{

}

void CSGNode::drawBoundingBox()
{
    glColor3d(1.0, 0.0, 0.0);
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(2, 0x0F0F);
    glBegin(GL_LINE_LOOP);
    glVertex2d(viewBox.x0, viewBox.y0);
    glVertex2d(viewBox.x1, viewBox.y0);
    glVertex2d(viewBox.x1, viewBox.y1);
    glVertex2d(viewBox.x0, viewBox.y1);
    glEnd();

    glDisable(GL_LINE_STIPPLE);
}

void CSGNode::drawControlPolygon(bool fill)
{
    for (int i = 0; i < numChild; i++)
    {
        ((CSGNode*)getChild(i))->drawControlPolygon(fill);
    }
}

void CSGNode::drawNaively(bool grid)
{
    if (emptyTestTree)
    {
        if (grid)
        {
            emptyTestTree->visitTreePreOrder(drawBoxBound);
        }
        else
        {
            emptyTestTree->visitTreePreOrder(drawBox);
        }
    }
}

void CSGNode::drawCasteljau(int level)
{
    for (int i = 0; i < numChild; i++)
    {
        ((CSGNode*)getChild(i))->drawCasteljau(level);
    }
}

void CSGNode::drawDomain(bool grid)
{
    for (int i = 0; i < numChild; i++)
    {
        ((CSGNode*)getChild(i))->drawDomain(grid);
    }
}

void CSGNode::drawBoxDomainTest(bool grid)
{
    for (int i = 0; i < numChild; i++)
    {
        ((CSGNode*)getChild(i))->drawBoxDomainTest(grid);
    }
}

void CSGNode::drawContour()
{
    for (int i = 0; i < numChild; i++)
    {
        ((CSGNode*)getChild(i))->drawContour();
    }
}

bool CSGNode::subdivideTestNode(TreeNode<StarTestBox> *node, bool critical, double threshold, int n, double * r)
{
    StarTestBox * box = node->getData();

    double ratio[3] = {0.5, 0.5, 0.5};
    if (n == 0)
    {
        r = ratio;
    }

    if (critical)
    {
        box->pos = BOX_CRITICAL;
    }
    else
    {
        box->pos = BOX_BOUNDARY;
    }

    if(box->isSmall(threshold))
    {
        return false;
    }
    else
    {
        double xm = (box->x0 + box->x1)/2;
        double ym = (box->y0 + box->y1)/2;
        double zm = (box->z0 + box->z1)/2;

        xm = box->x0 + r[0] * (box->x1 - box->x0);
        ym = box->y0 + r[1] * (box->y1 - box->y0);
        zm = box->z0 + r[2] * (box->z1 - box->z0);

        // binary tree
        if (box->y1 - box->y0 >= box->x1 - box->x0
                && box->y1 - box->y0 >= box->z1 - box->z0)
        {
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(box->x0, box->x1, box->y0, ym, box->z0, box->z1), node));
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(box->x0, box->x1, ym, box->y1, box->z0, box->z1), node));
        }
        else if (box->x1 - box->x0 >= box->z1 - box->z0
                 && box->x1 - box->x0 >= box->y1 - box->y0)
        {
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(box->x0, xm, box->y0, box->y1, box->z0, box->z1), node));
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(xm, box->x1, box->y0, box->y1, box->z0, box->z1), node));
        }
        else
        {
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(box->x0, box->x1, box->y0, box->y1, box->z0, zm), node));
            node->addChild(new TreeNode<StarTestBox>(StarTestBox(box->x0, box->x1, box->y0, box->y1, zm, box->z1), node));
        }

        for (int i = 0; i < node->getNumChild(); i++)
        {
            node->getChild(i)->getData()->critical = node->getData()->critical;
            node->getChild(i)->getData()->multiple = node->getData()->multiple;
        }

        // quad tree
//        node->addChild(new TreeNode<Box>(Box(box->x0, xm, box->y0, ym), node));
//        node->addChild(new TreeNode<Box>(Box(box->x0, xm, ym, box->y1), node));
//        node->addChild(new TreeNode<Box>(Box(xm, box->x1, box->y0, ym), node));
//        node->addChild(new TreeNode<Box>(Box(xm, box->x1, ym, box->y1), node));

        return true;
    }
}
